package pipeline.misc;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.*;

import javax.imageio.ImageIO;
import javax.vecmath.Color3f;
import javax.vecmath.Vector2f;

/**
 * This class holds all of the information necessary to describe texture data.
 * 
 * @author ags
 */
public class Texture {

    /** The name of the file from where the data was loaded */
    protected String filename;
    
    boolean sampleBiLinearly = false;
    public void useBiLinear(boolean y)
    {
    	if (y)
    		sampleBiLinearly = true;
    	else
    		sampleBiLinearly = false;
    }

    /** The width of the texture. */
    public int nx;

    /** The height of the texture. */
    public int ny;

    /** The RGB data for each pixel. */
    private byte[] cData;

    public Buffer cBuf;

    /**
     * Reads in a texture from the given file and stores the data. The image file
     * given must be stored in a format recognizable by javax.imageio.ImageIO.
     * 
     * @param imageFile The file where the texture is stored.
     */
    public Texture(File imageFile) {
        try {
            BufferedImage loadedImage = ImageIO.read(imageFile);

            nx = loadedImage.getWidth();
            ny = loadedImage.getHeight();
            cData = new byte[nx * ny * 3];

            System.out.print("loading " + nx + " x " + ny + " texture...");
            filename = imageFile.getName();

            int offset = 0;
            for (int iy = 0; iy < ny; iy++) {
                for (int ix = 0; ix < nx; ix++) {
                    int pixelValue = loadedImage.getRGB(ix, ny - 1 - iy);

                    cData[offset + 0] = (byte) (0xff & (pixelValue >> 16));
                    cData[offset + 1] = (byte) (0xff & (pixelValue >> 8));
                    cData[offset + 2] = (byte) (0xff & (pixelValue >> 0));

                    offset += 3;
                }
            }
            cBuf = ByteBuffer.wrap(cData);

            System.out.println("done.");
        }
        catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Samples this texture for a given 2D location. Places the result in the
     * color parameter.
     * 
     * @param p The 2D texture coordinate.
     * @param cOut The result of sampling the texture.
     */
    public void sample(Vector2f p, Color3f cOut) {
    	cOut.set(0, 0, 0);
    	if (sampleBiLinearly)
    	{
    		//the x coordinate of the pixel is a weighted average of the colors of the 4 ones around it
    		int txleft = (int) ((p.x*nx));
    		int txright = txleft + 1;
    		int tydown = (int) ((p.y*ny));
    		int tyup = tydown + 1;
    		double weightleftdown = (1-(p.y*ny - tydown)*(p.x*nx - txleft))/3;
    		double weightleftup = (1-(tyup - p.y*ny)*(p.x*nx - txleft))/3;
    		double weightrightup = (1-(tyup - p.y*ny)*(txright - p.x*nx))/3;
    		double weightrightdown = (1-(p.y*ny - tydown)*(txright - p.x*nx))/3;
    		
    		int txs[] = {txleft, txright};
    		int tys[] = {tydown, tyup};
    		double weights[] = {weightleftdown, weightleftup, weightrightdown, weightrightup};
    		int offset, ix, iy;
    		for (int i = 0; i < 2 ; i ++)
    		{
    			for (int j=0; j < 2; j++)
    			{
    				ix = Math.min(Math.max(txs[i], 0), nx - 1);
    		        iy = Math.min(Math.max(tys[j], 0), ny - 1);
    				offset = 3 * (nx * iy + ix);
    				cOut.x += (weights[2*i + j])*(cData[offset + 0] & 0xff) / 255.0f;
    		        cOut.y += (weights[2*i + j])*(cData[offset + 1] & 0xff) / 255.0f;
    		        cOut.z += (weights[2*i + j])*(cData[offset + 2] & 0xff) / 255.0f;
    				
    			}
    		}
    		
    	}
    	else
    	{
	        int ix = (int) (p.x * nx + 0.5);
	        int iy = (int) (p.y * ny + 0.5);
	        
	        ix = Math.min(Math.max(ix, 0), nx - 1);
	        iy = Math.min(Math.max(iy, 0), ny - 1);
	
	        int offset = 3 * (nx * iy + ix);
	        cOut.set((cData[offset + 0] & 0xff) / 255.0f, (cData[offset + 1] & 0xff) / 255.0f, (cData[offset + 2] & 0xff) / 255.0f);
    	}
    }
    
    /**
     * Samples the texture with bilinear interpolation
     * @param p
     * @param cOut
     */
    public void sampleBL(Vector2f p, Color3f cOut)
    {
    	cOut.set(0, 0.5f, 1);
    }

    /**
     * @see java.lang.Object#toString()
     */
    public String toString() {

        return filename;
    }

}